﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Xml;
using System.Globalization;

namespace XNAchievement.Achievements
{
    /// <summary>
    /// This class represents a single unspecific achievement.
    /// Use this Achivment type for simple "fire-and-forget" achievments
    /// like "complete level 5".
    /// </summary>
    [Serializable]
    public class Achievement
    {
        /// <summary>
        /// This event is fired, when the achievement is unlocked.
        /// Please note that the "Notification" event will also be
        /// fired. This event is just for simplification of some tasks.
        /// </summary>
        [field:NonSerialized] //We need this or the serializer try to serialize the backing field of the event (the subscribers)
        public event EventHandler Unlocked;

        /// <summary>
        /// This event is fired when the achievement has reached a status
        /// that might be communicated to the user.
        /// </summary>
        [field:NonSerialized]
        public event EventHandler<NotificationEventArgs> Notification;

        /// <summary>
        /// True when the achivement is not earned, false otherwise.
        /// Use the Unlock() method to change this value.
        /// </summary>
        public bool IsLocked { get; private set; }

        /// <summary>
        /// Gets a (optional) representive image for this achievement based on the locked status.
        /// </summary>
        public Texture2D Texture
        {
            get
            {
                return IsLocked ? LockedTexture : UnlockedTexture;
            }
        }

        /// <summary>
        /// Gets ot sets a representive image for this achievement when is is locked.
        /// </summary>
        public Texture2D LockedTexture
        {
            get { return lockedTexture; }
            set { lockedTexture = value; }
        }

        private readonly string lockedTextureName;
        [NonSerialized]
        private Texture2D lockedTexture;

        /// <summary>
        /// Gets ot sets a representive image for this achievement when is is unlocked.
        /// </summary>
        public Texture2D UnlockedTexture
        {
            get { return unlockedTexture; }
            set { unlockedTexture = value; }
        }

        private readonly string unlockedTextureName;
        [NonSerialized]
        private Texture2D unlockedTexture;

        /// <summary>
        /// When true, this achievement is only shown when it was earned.
        /// </summary>
        public bool VisibleWhenLocked { get; set; }

        /// <summary>
        /// The displayed name of this achievement.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The internal name for this achievement.
        /// </summary>
        public string InternalName { get; private set; }

        /// <summary>
        /// The DateTime when the achievement was unlocked.
        /// </summary>
        public DateTime DateTimeUnlocked { get; private set; }

        /// <summary>
        /// Gets the current (based on the locked status) description
        /// for this achievement.
        /// </summary>
        public string Description
        {
            get
            {
                return IsLocked ? LockedDescription : UnlockedDescription;
            }
        }

        /// <summary>
        /// Gets ot sets the description for this achievement when is is locked.
        /// </summary>
        public string LockedDescription { get; set; }

        /// <summary>
        /// Gets ot sets the description for this achievement when is is unlocked.
        /// </summary>
        public string UnlockedDescription { get; set; }

        /// <summary>
        /// Creates a locked achievements with the given internal name, using the given texture names.
        /// If no image is needed pass null for the name.
        /// </summary>
        public Achievement(string internalName, string lockedTextureName, string unlockedTextureName)
        {
            if (string.IsNullOrEmpty(internalName))
                throw new ArgumentNullException("internalName");

            InternalName = internalName;
            this.lockedTextureName = lockedTextureName;
            this.unlockedTextureName = unlockedTextureName;

            IsLocked = true;
            VisibleWhenLocked = true;
            Name = internalName;
            DateTimeUnlocked = DateTime.MinValue;
            LockedDescription = "";
            UnlockedDescription = "";
        }

        /// <summary>
        /// For internal use only.
        /// </summary>
        protected Achievement()
        {
        }

        /// <summary>
        /// Loads the Content for this achievement.
        /// </summary>
        public virtual void LoadContent(ContentManager content)
        {
            if (!string.IsNullOrEmpty(lockedTextureName))
                LockedTexture = content.Load<Texture2D>(lockedTextureName);

            if (!string.IsNullOrEmpty(unlockedTextureName))
                UnlockedTexture = content.Load<Texture2D>(unlockedTextureName);
        }

        /// <summary>
        /// Raises the "Unlocked" event.
        /// </summary>
        protected virtual void OnUnlocked()
        {
            if (Unlocked != null)
                Unlocked(this, null);
        }

        /// <summary>
        /// Raises the "Notification" event.
        /// </summary>
        protected virtual void OnNotification(NotificationEventArgs e)
        {
            if (Notification != null)
                Notification(this, e);
        }

        /// <summary>
        /// Unlocks this achievement.
        /// Overrides any additional logic in derived classes.
        /// </summary>
        public virtual void Unlock()
        {
            if (!IsLocked)
                return;

            IsLocked = false;
            DateTimeUnlocked = DateTime.Now;
            OnUnlocked();
            OnNotification(new NotificationEventArgs(NotificationType.Unlocked, null));
        }

        /// <summary>
        /// Converts this achievement to a string representation using the given IAchievementFormatter.
        /// </summary>
        public string AsString(IAchievementFormatter formatter)
        {
            return formatter.FormatAchievement(this);
        }
    }
}
